package com.nehc.nettyserver.netty;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.nehc.nettyserver.common.model.ClientInfo;
import com.nehc.nettyserver.common.model.Message;
import com.nehc.nettyserver.service.cache.CacheService;
import com.nehc.nettyserver.service.MessageService;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * 消息处理器
 * 
 * @author NEHC
 */
@Slf4j
@Component
@Sharable
@RequiredArgsConstructor
public class MessageHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
    
    private final ObjectMapper objectMapper;
    private final ChannelManager channelManager;
    private final CacheService cacheService;
    private final MessageService messageService;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (!(frame instanceof TextWebSocketFrame)) {
            log.warn("不支持的消息类型: {}", frame.getClass().getName());
            return;
        }
        
        String content = ((TextWebSocketFrame) frame).text();
        log.debug("收到消息: {}", content);
        
        try {
            Message message = objectMapper.readValue(content, Message.class);
            processMessage(ctx, message);
            
            // 更新客户端活跃时间
            String clientId = ctx.channel().attr(ChannelManager.ATTR_CLIENT_ID).get();
            if (clientId != null) {
                ClientInfo clientInfo = cacheService.get(CacheService.CacheType.CONNECTION, clientId);
                if (clientInfo != null) {
                    clientInfo.setLastActiveTime(System.currentTimeMillis());
                    clientInfo.setReceivedMessages(clientInfo.getReceivedMessages() + 1);
                    cacheService.put(CacheService.CacheType.CONNECTION, clientId, clientInfo);
                }
            }
        } catch (Exception e) {
            log.error("处理消息失败: {}", e.getMessage(), e);
            sendErrorResponse(ctx, "无效的消息格式", null);
        }
    }

    /**
     * 处理消息
     */
    private void processMessage(ChannelHandlerContext ctx, Message message) throws Exception {
        // 设置消息ID（如果客户端没有提供）
        if (message.getId() == null) {
            message.setId(UUID.randomUUID().toString());
        }
        
        // 设置消息时间（如果客户端没有提供）
        if (message.getTimestamp() == 0) {
            message.setTimestamp(System.currentTimeMillis());
        }
        
        String clientId = ctx.channel().attr(ChannelManager.ATTR_CLIENT_ID).get();
        if (clientId == null) {
            // 新连接未注册，自动注册
            clientId = UUID.randomUUID().toString();
            channelManager.register(ctx.channel(), clientId);
            log.info("新客户端自动注册: {}", clientId);
        }
        
        // 设置发送者ID
        message.setFrom(clientId);
        
        // 根据消息类型处理
        switch (message.getType()) {
            case AUTH:
                handleAuthMessage(ctx, message);
                break;
            case HEARTBEAT:
                handleHeartbeatMessage(ctx, message);
                break;
            case CHAT:
                handleChatMessage(ctx, message);
                break;
            case BROADCAST:
                handleBroadcastMessage(ctx, message);
                break;
            default:
                log.warn("未知的消息类型: {}", message.getType());
                sendErrorResponse(ctx, "未知的消息类型", message.getId());
        }
        
        // 持久化重要消息
        if (message.getType() != Message.Type.HEARTBEAT) {
            messageService.saveMessage(message);
        }
    }
    
    /**
     * 处理认证消息
     */
    private void handleAuthMessage(ChannelHandlerContext ctx, Message message) {
        String token = message.getPayload().get("token").toString();
        // 验证token的合法性（这里简化处理，实际应该调用认证服务）
        if (token != null && !token.isEmpty()) {
            // 假设token就是userId（实际应该解析token获取userId）
            String userId = token;
            channelManager.authorize(ctx.channel(), userId);
            
            // 返回认证成功响应
            Message response = Message.createResponse(message.getId(), Message.Type.AUTH);
            response.addPayload("success", true);
            response.addPayload("userId", userId);
            ctx.writeAndFlush(new TextWebSocketFrame(toJson(response)));
        } else {
            sendErrorResponse(ctx, "认证失败，无效的Token", message.getId());
        }
    }
    
    /**
     * 处理心跳消息
     */
    private void handleHeartbeatMessage(ChannelHandlerContext ctx, Message message) {
        // 返回心跳响应
        Message response = Message.createResponse(message.getId(), Message.Type.HEARTBEAT);
        response.addPayload("time", System.currentTimeMillis());
        ctx.writeAndFlush(new TextWebSocketFrame(toJson(response)));
    }
    
    /**
     * 处理聊天消息
     */
    private void handleChatMessage(ChannelHandlerContext ctx, Message message) {
        // 检查目标用户ID
        if (!message.getPayload().containsKey("to")) {
            sendErrorResponse(ctx, "聊天消息必须指定接收者", message.getId());
            return;
        }
        
        String toUserId = message.getPayload().get("to").toString();
        
        // 发送消息给目标用户
        int count = channelManager.sendToUser(toUserId, toJson(message));
        if (count > 0) {
            // 返回发送成功响应
            Message response = Message.createResponse(message.getId(), Message.Type.CHAT);
            response.addPayload("success", true);
            response.addPayload("delivered", count);
            ctx.writeAndFlush(new TextWebSocketFrame(toJson(response)));
        } else {
            sendErrorResponse(ctx, "用户不在线或消息发送失败", message.getId());
        }
    }
    
    /**
     * 处理广播消息
     */
    private void handleBroadcastMessage(ChannelHandlerContext ctx, Message message) {
        // 检查是否有权限发送广播（只有已授权用户可以广播）
        Boolean authorized = ctx.channel().attr(ChannelManager.ATTR_AUTHORIZED).get();
        if (authorized == null || !authorized) {
            sendErrorResponse(ctx, "未授权，无法发送广播消息", message.getId());
            return;
        }
        
        // 发送广播
        int count = channelManager.broadcast(toJson(message));
        
        // 返回广播结果
        Message response = Message.createResponse(message.getId(), Message.Type.BROADCAST);
        response.addPayload("success", true);
        response.addPayload("recipients", count);
        ctx.writeAndFlush(new TextWebSocketFrame(toJson(response)));
    }
    
    /**
     * 发送错误响应
     */
    private void sendErrorResponse(ChannelHandlerContext ctx, String errorMessage, String requestId) {
        Message response = Message.createErrorResponse(requestId, errorMessage);
        ctx.writeAndFlush(new TextWebSocketFrame(toJson(response)));
    }
    
    /**
     * 将对象转换为JSON字符串
     */
    private String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            log.error("JSON序列化失败: {}", e.getMessage(), e);
            return "{}";
        }
    }
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 连接建立，分配随机客户端ID
        String clientId = UUID.randomUUID().toString();
        channelManager.register(ctx.channel(), clientId);
        
        // 发送欢迎消息
        Message welcome = new Message();
        welcome.setId(UUID.randomUUID().toString());
        welcome.setType(Message.Type.SYSTEM);
        welcome.setTimestamp(System.currentTimeMillis());
        welcome.addPayload("clientId", clientId);
        welcome.addPayload("message", "Welcome to NEHC Netty Server!");
        
        ctx.writeAndFlush(new TextWebSocketFrame(toJson(welcome)));
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        // 连接关闭，注销客户端
        channelManager.unregister(ctx.channel(), "连接关闭");
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("通道异常: {}", cause.getMessage(), cause);
        channelManager.unregister(ctx.channel(), "通道异常: " + cause.getMessage());
    }
} 